package dapp

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"os"

	"github.com/libp2p/go-libp2p-core/crypto"
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/spf13/cobra"
	"hundsun.com/hsl/hschain/cmd/cli/common"
	"hundsun.com/hsl/hschain/common/util"
	"hundsun.com/hsl/hschain/p2p/common/utils"
	protosp2p "hundsun.com/hsl/hschain/protos/p2p"
	service "hundsun.com/hsl/hschain/rpc/impl"
)

// NetCmd net command
func NetCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "net",
		Short: "Net operation",
		Args:  cobra.MinimumNArgs(1),
	}

	cmd.AddCommand(
		GetPeerCountCmd(),
		GetPeerIDCmd(),
		GetPeerInfosCmd(),
		generatePeerCmd(),
		showPeerIDCmd(),
	)

	return cmd
}

// GetPeerCountCmd  get peers connected count
func GetPeerCountCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "peerCount",
		Short: "Get peer count",
		Run:   peerCount,
	}

	return cmd
}

// GetPeerIDCmd  get peer ID
func GetPeerIDCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "peerID",
		Short: "Get peer ID",
		Run:   peerID,
	}
	return cmd
}

//GetPeerInfosCmd 获取连接节点信息
func GetPeerInfosCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "peerInfos",
		Short: "Get peer infos",
		Run:   peerInfos,
	}
	return cmd
}

func peerCount(cmd *cobra.Command, args []string) {
	rpcLaddr, _ := cmd.Flags().GetString(common.FlagRPCLaddr)
	common.RPCCall(rpcLaddr, service.GetPeerCount, nil, nil)
}

func peerID(cmd *cobra.Command, args []string) {
	rpcLaddr, _ := cmd.Flags().GetString(common.FlagRPCLaddr)
	common.RPCCall(rpcLaddr, service.GetP2pPeerID, nil, nil)
}

func peerInfos(cmd *cobra.Command, args []string) {
	rpcLaddr, _ := cmd.Flags().GetString(common.FlagRPCLaddr)
	common.RPCCall(rpcLaddr, service.GetP2pPeerInfos, nil, func(res interface{}) {
		obj := res.(service.JSONRPCResp)
		val, _ := util.Bytes2Struct(&protosp2p.PeerInfos{}, obj.Payload)
		common.PrintJSON(val)
	})
}

func generatePeerCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "generate",
		Short: "Generate peer key",
		Long:  "Generate peer key",
		Run:   generatePeerID,
	}
	cmd.Flags().String("private", "private.key", "private key file output path")
	cmd.Flags().String("public", "public.key", "public key file output path")

	return cmd
}

func generatePeerID(cmd *cobra.Command, args []string) {

	priPath, _ := cmd.Flags().GetString("private")
	pubPath, _ := cmd.Flags().GetString("public")

	priv, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		fmt.Printf("generate key failed, reason:%v\n", err)
		return
	}
	privBuf, err := x509.MarshalPKCS8PrivateKey(priv)
	if err != nil {
		fmt.Printf("marshal private key failed, reason:%v\n", err)
		return
	}
	pubvBuf, err := x509.MarshalPKIXPublicKey(priv.Public())
	if err != nil {
		fmt.Printf("marshal public key failed, reason:%v\n", err)
		return
	}
	priOut, err := os.Create(priPath)
	if err != nil {
		fmt.Printf("create private key failed, reason:%v\n", err)
		return
	}
	pubOut, err := os.Create(pubPath)
	if err != nil {
		fmt.Printf("create public key failed, reason:%v\n", err)
		return
	}
	pub, err := crypto.UnmarshalRsaPublicKey(pubvBuf)
	if err != nil {
		fmt.Printf("unmarshal public key failed, reason:%v\n", err)
		return
	}
	peerID, err := peer.IDFromPublicKey(pub)
	if err != nil {
		fmt.Printf("get id failed, reason:%v\n", err)
		return
	}
	fmt.Printf("peer id is: %v\n", peerID)

	pem.Encode(priOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: privBuf})
	pem.Encode(pubOut, &pem.Block{Type: "RSA PUBLIC KEY", Bytes: pubvBuf})

}

func showPeerIDCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "show",
		Short: "Show peerID",
		Long:  "Show peerID",
		Run:   showPeerID,
	}
	cmd.Flags().StringP("public", "p", "", "public key file path")
	cmd.Flags().StringP("private", "v", "", "private key file path")
	return cmd
}

func showPeerID(cmd *cobra.Command, args []string) {
	pubPath, _ := cmd.Flags().GetString("public")
	priPath, _ := cmd.Flags().GetString("private")

	var id peer.ID
	var err error
	if pubPath != "" {
		id, err = utils.IDFromPublicKey(pubPath)
		if err != nil {
			fmt.Printf("get peer id from public key failed, reason:%s\n", err.Error())
			return
		}
	} else if priPath != "" {
		id, err = utils.IDFromPrivateKey(priPath)
		if err != nil {
			fmt.Printf("get peer id from private key failed, reason:%s\n", err.Error())
			return
		}
	} else {
		fmt.Println("private key and public key is nil")
		return
	}

	fmt.Printf("peer id is: %s\n", id.String())
}
